package cn.comtom.ewbs.signature.service.impl;
import java.util.Date;
import java.util.List;
import java.util.Map;

import javax.xml.bind.DatatypeConverter;

import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Maps;
import com.union.shm.UnionCertAppAPI;

import cn.comtom.ewbs.signature.common.exception.SignException;
import cn.comtom.ewbs.signature.common.utils.ConfigFileUtil;
import cn.comtom.ewbs.signature.common.vo.request.CertApplyReq;
import cn.comtom.ewbs.signature.common.vo.response.CertResp;
import cn.comtom.ewbs.signature.service.AbstractSignService;
import cn.hutool.core.util.HexUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service("shm1814")
public class Shm1814ServiceImpl extends AbstractSignService implements InitializingBean, DisposableBean{
	
	private static UnionCertAppAPI api;
	
	private static final String HEX_CHAR = "0123456789ABCDEF";
	
	private static final byte[] HEX_STRING_BYTE = HEX_CHAR.getBytes();
	
    /** 
     * 用于建立十六进制字符的输出的大写字符数组 
     */  
    private static final char[] DIGITS_UPPER = { '0', '1', '2', '3', '4', '5',  
            '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; 
	
	public Shm1814ServiceImpl() {
		api = new UnionCertAppAPI();
	};

	public Shm1814ServiceImpl(String ip, int port, String SNHEX) {
		api = new UnionCertAppAPI();
		UnionCertAppAPI.init(ip, port, SNHEX);
	}
	
	/**
     * 将时间转换为16进制
     * @return
     */
    public static String dateTo16Hex(Date date) {
        Long ab = date.getTime()/1000;
        String a = Long.toHexString(ab);
        
        return a;
    }
	public static String UTC(){
		return dateTo16Hex(new Date()).toUpperCase();
	}
    /**
     * 将16进制转换为时间
     * @return
     */
    public static Date hexToDate(String hexStr) {
        Long ab = Long.valueOf(hexStr, 16);
        ab=ab*1000;
        Date date = new Date();
        date.setTime(ab);
        return date;
    }
    
	/**
	 * 十六进制字符串转为字节数组
	 * @param aschexByte
	 * @return
	 */
	private byte[] aschex_to_bcdhex(byte[] aschexByte) {
		int j = 0;
		if (aschexByte.length % 2 == 0) {
			j = aschexByte.length / 2;
			byte[] resTmp = new byte[j];
			for (int i = 0; i < j; i++) {
				resTmp[i] = ascToHex(aschexByte[2 * i], aschexByte[2 * i + 1]);
			}
			return resTmp;

		} else {
			j = aschexByte.length / 2 + 1;
			byte[] resTmp = new byte[j];
			for (int i = 0; i < j - 1; i++) {
				resTmp[i] = ascToHex((byte) aschexByte[2 * i],
						(byte) aschexByte[2 * i + 1]);
			}
			resTmp[j - 1] = ascToHex((byte) aschexByte[2 * (j - 1)], (byte) 0);
			return resTmp;
		}
	}

	private byte ascToHex(byte ch1, byte ch2) {
		byte ch;
		if (ch1 >= 'A')
			ch = (byte) ((ch1 - 0x37) << 4);
		else
			ch = (byte) ((ch1 - '0') << 4);
		if (ch2 >= 'A')
			ch |= (byte) (ch2 - 0x37);
		else
			ch |= (byte) (ch2 - '0');
		return ch;
	}

	@Override
	public String getByteSign(byte[] data) throws Exception {
		log.info("getByteSign use:{},dataType:{}",signType,dataType);
		String signHEX = null;
		if(dataType==1) {
			signHEX = api.Platform_CalculateSignature(dataType, data);
			signHEX = HexUtil.encodeHexStr(Base64Utils.decodeFromString(signHEX));
		}else {
			String utc = UTC();
			String hex16Str = new String(encodeHex(data)).concat(utc);
			data = hex16Str.getBytes();
			log.info("api.Platform_CalculateSignature hex16Str:{}",hex16Str);
			data = aschex_to_bcdhex(data);
			signHEX = api.Platform_CalculateSignature(dataType, data);
			if (api.getErrorCode() != 0) {
				log.error("getByteSign fail! Errcode:{},ErrMessage:{}",api.getErrorCode(),api.getErrorMessage());
				return null;
			}
			signHEX = HexUtil.encodeHexStr(Base64Utils.decodeFromString(signHEX));
			signHEX = utc.concat(signHEX);
		}
		return signHEX;
	}

	@Override
	public Boolean verifyByteSign(byte[] data, String signValue) throws Exception {
		log.info("verifyByteSign use:{},dataType:{}",signType,dataType);
		int ret = 0;
		if(dataType==1) {
			signValue = Base64Utils.encodeToString(HexUtil.decodeHex(signValue));
			ret = api.Platform_VerifySignature(dataType, data, signValue);
		}else {
			String utc = signValue.substring(0, 8);
			signValue = signValue.substring(8);
			String hex16Str = new String(encodeHex(data)).concat(utc);
			data = hex16Str.getBytes();
			log.info("api.Platform_VerifySignature hex16Str:{}",hex16Str);
			data = aschex_to_bcdhex(data);
			signValue = Base64Utils.encodeToString(HexUtil.decodeHex(signValue));
			ret = api.Platform_VerifySignature(dataType, data, signValue);
		}
		if (ret != 0) {
			log.error("verifyByteSign fail! Errcode:{},ErrMessage:{}",api.getErrorCode(),api.getErrorMessage());
			return false;
		}
		return true;
	}

	@Override
	public String getFileSign(byte[] data) throws Exception {
		log.info("getFileSign use:{},dataType:{}",signType,dataType);
		String signHEX = null;
		if(dataType==1) {
			signHEX = api.Platform_CalculateSignature(dataType, data);
		}else {
			String utc = UTC();
			String hex16Str = new String(encodeHex(data)).concat(utc);
			data = aschex_to_bcdhex(hex16Str.getBytes());
			signHEX = api.Platform_CalculateSignature(dataType, data);
			if (api.getErrorCode() != 0) {
				log.error("getFileSign fail! Errcode:{},ErrMessage:{}",api.getErrorCode(),api.getErrorMessage());
				return null;
			}
			signHEX = HexUtil.encodeHexStr(Base64Utils.decodeFromString(signHEX));
			signHEX = utc.concat(signHEX);
			signHEX = Base64Utils.encodeToString(HexUtil.decodeHex(signHEX));
		}
		return signHEX;
	}

	@Override
	public Boolean verifyFileSign(byte[] data, String signValue) throws Exception {
		log.info("verifyFileSign use:{},dataType:{}",signType,dataType);
		int ret = 0;
		if(dataType==1) {
			ret = api.Platform_VerifySignature(dataType, data, signValue);	
		}else {
			String signValue64 = HexUtil.encodeHexStr(Base64Utils.decodeFromString(signValue));
			String utc = signValue64.substring(0, 8);
			signValue = Base64Utils.encodeToString(HexUtil.decodeHex(signValue64.substring(8)));
			String hex16Str = new String(encodeHex(data)).concat(utc);
			data = aschex_to_bcdhex(hex16Str.getBytes());
			ret = api.Platform_VerifySignature(0, data, signValue);
		}
		if (ret != 0) {
			log.error("verifyFileSign fail! Errcode:{},ErrMessage:{}",api.getErrorCode(),api.getErrorMessage());
			return false;
		}
		return true;
	}
	
	@Override
	public CertResp certApply(CertApplyReq certApplyReq) throws Exception {
		CertResp certResp = new CertResp();
		String ptr = null;
		
		byte[] cert = null;
		String certHEX = "";

		//ptr = UnionInput("请输入接受者编号::");
		byte[] ReceiverSN = aschex_to_bcdhex(certApplyReq.getDestCertNo().getBytes());
		
		//ptr = UnionInput("请输入授权列表序列号::");
		byte[] CertsAuthSN = aschex_to_bcdhex(ptr.getBytes());
		
		//ptr = UnionInput("请输入序列号数目::");
		int Count = certApplyReq.getSourceCertNos().size();
		
		//ptr = UnionInput("请输入序列号集合::");
		byte[] SNs = aschex_to_bcdhex(ptr.getBytes());
		
		//ptr = UnionInput("请输入颁发者序列号::");
		byte[] signSN = aschex_to_bcdhex(certApplyReq.getDestCertNo().getBytes());
		
		cert = api.SignBC_CertAuth(ReceiverSN, CertsAuthSN, Count, SNs, signSN);
		if (api.getErrorCode() != 0) {
			log.error("verifyFileSign fail! Errcode:{},ErrMessage:{}",api.getErrorCode(),api.getErrorMessage());
		}
		return certResp;
	}

	@Override
	public boolean openDevice() {
		try {
			api.OpenDevice();
			return true;
		} catch (Exception e) {
			log.error("open device shm1814 failed {}", e);
			throw new SignException(String.format("打开shm1814设备失败-%s", e.getMessage()));
		}
	}

	@Override
	public void closeDevice() {
		try {
			api.CloseDevice();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	@Override
	public void destroy() {
		closeDevice();
	}

	@Override
	public void afterPropertiesSet() {
		//openDevice();
	}

	@Override
	public Boolean importCert(List<String> certs) {
		log.info("importCert use:{},certs:{}",signType,JSON.toJSONString(certs));
		int ret = 0;
		try {
			for (String cert : certs) {
				int vkindex = 0;
				int ischecklink = 1;
				int certtype = 2;
				byte[] certData = aschex_to_bcdhex(cert.getBytes());
				ret = api.updateCert(certData, vkindex, ischecklink,certtype);
				if(ret != 0) {
					log.error("importCert failed! Errcode:{},ErrMessage:{}",api.getErrorCode(),api.getErrorMessage());
					return false;	
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return true;
	}

	@Override
	public Boolean importCertauthList(String certauthList) {
		log.info("importCertauthList use:{},certauthList:{}",signType,certauthList);
		int ret = 0;
		try {
			byte[] CertAuth = aschex_to_bcdhex(certauthList.getBytes());
			ret = api.updateCertAuth(CertAuth);
			if (ret != 0) {
				log.error("importCertauthList failed! Errcode:{},ErrMessage:{}",api.getErrorCode(),api.getErrorMessage());
				return false;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return true;
	}
	
	/**
	 * 10进制字节数组转换为16进制字节数组
	 * 
	 * byte用二进制表示占用8位，16进制的每个字符需要用4位二进制位来表示，则可以把每个byte
	 * 转换成两个相应的16进制字符，即把byte的高4位和低4位分别转换成相应的16进制字符，再取对应16进制字符的字节
	 * 
	 * @param b 10进制字节数组
	 * @return 16进制字节数组
	 */
	public  byte[] byte2hex(byte[] b) {
		int length = b.length;
		byte[] b2 = new byte[length << 1];
		int pos;
		for(int i=0; i<length; i++) {
			pos = 2*i;
			b2[pos] = HEX_STRING_BYTE[(b[i] & 0xf0) >> 4];
			b2[pos+1] = HEX_STRING_BYTE[b[i] & 0x0f];
		}
		return b2;
	}
	
	public  char[] encodeHex(byte[] data) {  
        return encodeHex(data, DIGITS_UPPER);  
    }
	
	protected  char[] encodeHex(byte[] data, char[] toDigits) {  
        int l = data.length;  
        char[] out = new char[l << 1];  
        // two characters form the hex value.  
        for (int i = 0, j = 0; i < l; i++) {  
            out[j++] = toDigits[(0xF0 & data[i]) >>> 4];  
            out[j++] = toDigits[0x0F & data[i]];  
        }  
        return out;  
    } 
	
	
	
	@Override
	public boolean updateConfig(String configFilePath, String ip, Integer port, String signNo, String logPath, Integer conLimit, Integer timeOut) {
		Map<String, String> configMap = Maps.newHashMap();
		configMap.put("ipAddrOfCenterSecuSvr", ip);
		configMap.put("portOfCenterSecuSvr", port.toString());
		configMap.put("SignSN", signNo);
		configMap.put("UNIONLOG", logPath);
		configMap.put("timeoutOfCenterSecuSvr", timeOut.toString());
		return ConfigFileUtil.updateConfFile(configFilePath, configMap);
	}

	/**
	 * @param args
	 * @throws Exception
	 */
	public static void main(String[] args) throws Exception {
		System.out.println(hexToDate("01BB05A3"));
		String printBase64Binary1 = "YVKm/gAAAAJelwtU7ReFLg9Ygq2RLOjBo8JwnZuT8BE4EIuuK08ewdwYS8q6t8W5Ua8mG+yxvPDoOKjVcjpyONDBQ+dSg2eUM+s=";
		String printBase64Binary2="AABCtgAAVAE+jD7sGQla0bPUcgTyZzdeZgTZQc3EklUbnKUJK74tOwtFzkj50LnIaq81+O3DvsQEzDrk+VuH1rWqBY3JNDoiRDs=";
		String signValue641 = HexUtil.encodeHexStr(Base64Utils.decodeFromString(printBase64Binary1));
		String signValue642 = HexUtil.encodeHexStr(Base64Utils.decodeFromString(printBase64Binary2));
		System.out.println(signValue641);
		System.out.println(signValue642);
		
		String utc = Base64Utils.encodeToString(HexUtil.decodeHex(UTC()));
		System.out.println(utc);
		
		System.out.println(new Shm1814ServiceImpl().aschex_to_bcdhex(HEX_STRING_BYTE));
		System.out.println(printBase64Binary1);
		System.out.println(DatatypeConverter.printHexBinary(DatatypeConverter.parseBase64Binary(printBase64Binary1)));
		//Srj1401ServiceImpl apitest = new Srj1401ServiceImpl("127.0.0.1",6000,"0000000059DA");
		Shm1814ServiceImpl apitest = new Shm1814ServiceImpl();
		apitest.openDevice();
		String signStr = "1234";
		System.out.println(signStr);
		byte[] data = signStr.getBytes();
		String byteSign = apitest.getByteSign(data);
		Boolean verifyByteSign = apitest.verifyByteSign(data, byteSign);
		System.out.println(byteSign);
		System.out.println(verifyByteSign);
	}

}